package org.ohos.rajawali3d.examples.examples.interactive;

import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.agp.render.opengl.GLES1X;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.utils.TextAlignment;
import ohos.agp.window.service.WindowManager;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;
import opengl.platform.ohos.GLES20;
import org.ohos.rajawali3d.ResourceTable;
import org.ohos.rajawali3d.examples.examples.AExampleFragment;
import org.rajawali3d.Object3D;
import org.rajawali3d.lights.DirectionalLight;
import org.rajawali3d.materials.Material;
import org.rajawali3d.materials.methods.DiffuseMethod;
import org.rajawali3d.math.Matrix4;
import org.rajawali3d.math.vector.Vector3;
import org.rajawali3d.primitives.Sphere;
import org.rajawali3d.util.GLU;
import org.rajawali3d.util.ObjectColorPicker;
import org.rajawali3d.util.OnObjectPickedListener;
import org.rajawali3d.util.RajLog;

public class TouchAndDragFragment extends AExampleFragment implements
        Component.TouchEventListener {

    @Override
    public void onStart(Intent savedInstanceState) {
        super.onStart(savedInstanceState);
    }

    @Override
    public Component onCreateView(LayoutScatter inflater, ComponentContainer container,
                                  Intent savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        ((Component) mRenderSurface).setTouchEventListener(this);

        DirectionalLayout ll = new DirectionalLayout(getContext());
        ComponentContainer.LayoutConfig lc = ll.getLayoutConfig();
        lc.width = DirectionalLayout.LayoutConfig.MATCH_PARENT;
        lc.height = DirectionalLayout.LayoutConfig.MATCH_PARENT;
        ll.setOrientation(DirectionalLayout.VERTICAL);
        ll.setAlignment(LayoutAlignment.BOTTOM);

        Text label = new Text(getContext());
        label.setText(ResourceTable.String_touch_and_drag_fragment_info);
        label.setTextSize(50);
        label.setTextAlignment(TextAlignment.HORIZONTAL_CENTER);
        label.setHeight(80);
        label.setTextColor(Color.WHITE);
        label.setWidth(DirectionalLayout.LayoutConfig.MATCH_PARENT);
        ll.addComponent(label);
        mLayout.addComponent(ll);
        mLayout.moveChildToFront(ll);
        mRenderSurface.setTransparent(false);
        WindowManager.getInstance().getTopWindow().get().setTransparent(true);

        return mLayout;
    }

    @Override
    public AExampleRenderer createRenderer() {
        return new TouchAndDragRenderer(getContext(), this);
    }

    @Override
    public boolean onTouchEvent(Component v, TouchEvent event) {
        switch (event.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                ((TouchAndDragRenderer) mRenderer).getObjectAt(event.getPointerPosition(0).getX(),
                        event.getPointerPosition(0).getY());
                break;
            case TouchEvent.POINT_MOVE:
                ((TouchAndDragRenderer) mRenderer).moveSelectedObject(event.getPointerPosition(0).getX(),
                        event.getPointerPosition(0).getY());
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                ((TouchAndDragRenderer) mRenderer).stopMovingSelectedObject();
                break;
        }
        return true;
    }

    private final class TouchAndDragRenderer extends AExampleRenderer implements
            OnObjectPickedListener {
        private ObjectColorPicker mPicker;
        private Object3D mSelectedObject;
        private int[] mViewport;
        private double[] mNearPos4;
        private double[] mFarPos4;
        private Vector3 mNearPos;
        private Vector3 mFarPos;
        private Vector3 mNewObjPos;
        private Matrix4 mViewMatrix;
        private Matrix4 mProjectionMatrix;

        public TouchAndDragRenderer(Context context, AExampleFragment fragment) {
            super(context, fragment);
        }

        @Override
        protected void initScene() {
            mViewport = new int[]{0, 0, getViewportWidth(), getViewportHeight()};
            mNearPos4 = new double[4];
            mFarPos4 = new double[4];
            mNearPos = new Vector3();
            mFarPos = new Vector3();
            mNewObjPos = new Vector3();
            mViewMatrix = getCurrentCamera().getViewMatrix();
            mProjectionMatrix = getCurrentCamera().getProjectionMatrix();

            mPicker = new ObjectColorPicker(this);
            mPicker.setOnObjectPickedListener(this);

            try {
                DirectionalLight light = new DirectionalLight(-1, 0, -1);
                light.setPower(1.5f);
                getCurrentScene().addLight(light);
                getCurrentCamera().setPosition(0, 0, 4);

                Material material = new Material();
                material.enableLighting(true);
                material.setDiffuseMethod(new DiffuseMethod.Lambert());

                for (int i = 0; i < 20; i++) {
                    Sphere sphere = new Sphere(.3f, 12, 12);
                    sphere.setMaterial(material);
                    sphere.setColor(0x333333 + (int) (Math.random() * 0xcccccc));
                    sphere.setX(-4 + (Math.random() * 8));
                    sphere.setY(-4 + (Math.random() * 8));
                    sphere.setZ(-2 + (Math.random() * -6));
                    sphere.setDrawingMode(GLES20.GL_TRIANGLES);
                    mPicker.registerObject(sphere);
                    getCurrentScene().addChild(sphere);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public void onRenderSurfaceSizeChanged(GLES1X gl, int width, int height) {
            super.onRenderSurfaceSizeChanged(gl, width, height);
            mViewport[2] = getViewportWidth();
            mViewport[3] = getViewportHeight();
            mViewMatrix = getCurrentCamera().getViewMatrix();
            mProjectionMatrix = getCurrentCamera().getProjectionMatrix();
        }

        public void getObjectAt(float x, float y) {
            mPicker.getObjectAt(x, y);
        }

        public void onObjectPicked(Object3D object) {
            mSelectedObject = object;
        }

        @Override
        public void onNoObjectPicked() {
            RajLog.w("No object picked!");
        }

        public void moveSelectedObject(float x, float y) {
            if (mSelectedObject == null)
                return;

            //
            // -- unproject the screen coordinate (2D) to the camera's near plane
            //

            GLU.gluUnProject(x, getViewportHeight() - y, 0, mViewMatrix.getDoubleValues(), 0,
                    mProjectionMatrix.getDoubleValues(), 0, mViewport, 0, mNearPos4, 0);

            //
            // -- unproject the screen coordinate (2D) to the camera's far plane
            //

            GLU.gluUnProject(x, getViewportHeight() - y, 1.f, mViewMatrix.getDoubleValues(), 0,
                    mProjectionMatrix.getDoubleValues(), 0, mViewport, 0, mFarPos4, 0);

            //
            // -- transform 4D coordinates (x, y, z, w) to 3D (x, y, z) by dividing
            // each coordinate (x, y, z) by w.
            //

            mNearPos.setAll(mNearPos4[0] / mNearPos4[3], mNearPos4[1]
                    / mNearPos4[3], mNearPos4[2] / mNearPos4[3]);
            mFarPos.setAll(mFarPos4[0] / mFarPos4[3],
                    mFarPos4[1] / mFarPos4[3], mFarPos4[2] / mFarPos4[3]);

            //
            // -- now get the coordinates for the selected object
            //

            double factor = (Math.abs(mSelectedObject.getZ()) + mNearPos.z)
                    / (getCurrentCamera().getFarPlane() - getCurrentCamera()
                    .getNearPlane());

            mNewObjPos.setAll(mFarPos);
            mNewObjPos.subtract(mNearPos);
            mNewObjPos.multiply(factor);
            mNewObjPos.add(mNearPos);

            mSelectedObject.setX(mNewObjPos.x);
            mSelectedObject.setY(mNewObjPos.y);
        }

        public void stopMovingSelectedObject() {
            mSelectedObject = null;
        }

    }

}
